package middleware

import (
	"encoding/gob"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"net/http"
	"strconv"
	"strings"
	"time"
	"webook/internal/web"
)

type LoginMiddlewareJWTBuilder struct {
	// 跳过登录校验的路径
	paths []string
}

func NewLoginMiddlewareJWTBuilder() *LoginMiddlewareJWTBuilder {
	return &LoginMiddlewareJWTBuilder{
		paths: []string{},
	}
}
func (l *LoginMiddlewareJWTBuilder) Ignore(path string) *LoginMiddlewareJWTBuilder {
	l.paths = append(l.paths, path)
	return l
}

// 登录状态检查
func (l *LoginMiddlewareJWTBuilder) LoginCheck() gin.HandlerFunc {
	gob.Register(time.Now())
	return func(ctx *gin.Context) {
		//跳过登录校验的路径
		for _, path := range l.paths {
			if ctx.Request.URL.Path == path {
				return
			}
		}

		//从请求头中获取token
		tokenStr := ctx.GetHeader("Authorization")
		if len(tokenStr) == 0 {
			ctx.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		//解析token
		auth := strings.Split(tokenStr, " ")
		//TODO: Bearer是什么意思？
		//Bearer是一种身份验证协议，用于在HTTP请求中传递安全的凭据。
		//它通常用于API身份验证，其中客户端通过发送包含凭据的Bearer令牌来证明其身份。
		//Bearer令牌是一个字符串，由三部分组成：Bearer、空格和令牌本身。
		//Bearer令牌的格式通常为：Bearer <token>
		//在HTTP请求中，客户端通常将Bearer令牌作为Authorization头的值发送。
		//服务器接收到请求后，会检查Authorization头的值是否以Bearer开头，如果是，则认为这是一个Bearer令牌。
		//服务器会将Bearer令牌后面的部分作为实际的令牌进行验证。
		//Bearer令牌的主要优点是它是安全的，因为它是通过HTTPS协议发送的，并且通常是加密的。
		//它还可以用于跨域请求，因为它是一种身份验证协议，而不是一种身份验证机制。
		if len(auth) != 2 || auth[0] != "Bearer" {
			ctx.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		claims := web.UserClaims{}
		tokenStr = auth[1]
		//下面这段是做什么的
		token, err := jwt.ParseWithClaims(tokenStr, &claims, func(token *jwt.Token) (interface{}, error) {
			//该回调函数用于提供验证令牌签名所需的密钥。在验证过程中，jwt.Parse 函数会调用这个回调函数来获取密钥
			return []byte(web.JwtKey), nil
		})
		if err != nil {
			ctx.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		//检查token是否有效
		if token == nil || !token.Valid {
			ctx.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		//刷新jwt token过期时间
		ExpirationTime, err := claims.GetExpirationTime()
		if err != nil {
			ctx.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		//过期时间已经在 当前时间之前 代表已经过期
		if ExpirationTime.Before(time.Now()) {
			ctx.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		//每10秒刷新一次
		if ExpirationTime.Sub(time.Now()) < time.Second*10 {
			claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute))
			newToken, err := token.SignedString([]byte(web.JwtKey))
			if err != nil {
				ctx.AbortWithStatus(http.StatusUnauthorized)
				return
			} else {
				ctx.Header("X-Jwt-Token", newToken)
			}
		}

		//可以把Id设置进Header中，方便我们后续profile和edit
		ctx.Header("Id", strconv.FormatInt(int64(claims.Id), 10))
	}
}
